/*
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"

#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/api.h"
#include "lwip/ip_addr.h"

#include "feather_esp.h"

#define EXAMPLE_ESP_WIFI_SSID      "Feather"//CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS      "12345678"//CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_MAX_STA_CONN       1//CONFIG_MAX_STA_CONN
#define TCPIP_PORT                 8888

#ifdef USE_WIFI_AP

static const char *tag = "FEATHER_ESP32";
static const char bin2hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F',0};

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch(event->event_id) 
    {
        case SYSTEM_EVENT_AP_STACONNECTED:
            ESP_LOGI(tag, "station:"MACSTR" join, AID=%d", MAC2STR(event->event_info.sta_connected.mac), event->event_info.sta_connected.aid);
            break;
        case SYSTEM_EVENT_AP_STADISCONNECTED:
            ESP_LOGI(tag, "station:"MACSTR"leave, AID=%d", MAC2STR(event->event_info.sta_disconnected.mac), event->event_info.sta_disconnected.aid);
            break;
        default:
            break;
    }
    return ESP_OK;
}

void wifi_init_softap(void)
{
    s_wifi_event_group = xEventGroupCreate();

    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    wifi_config_t wifi_config = 
    {
        .ap = 
        {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) 
    {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    uint8_t wifi_mac[6] = {0};
    esp_wifi_get_mac(ESP_IF_WIFI_AP,wifi_mac);
    ESP_LOGI(tag, "wifi_mac: %02X:%02X:%02X:%02X:%02X:%02X", wifi_mac[0],wifi_mac[1],wifi_mac[2],wifi_mac[3],wifi_mac[4],wifi_mac[5]);

    wifi_config.ap.ssid[wifi_config.ap.ssid_len    ] = bin2hex[((wifi_mac[4]>>4)&0x0f)];
    wifi_config.ap.ssid[wifi_config.ap.ssid_len + 1] = bin2hex[((wifi_mac[4])&0x0f)];
    wifi_config.ap.ssid[wifi_config.ap.ssid_len + 2] = bin2hex[((wifi_mac[5]>>4)&0x0f)];
    wifi_config.ap.ssid[wifi_config.ap.ssid_len + 3] = bin2hex[((wifi_mac[5])&0x0f)];
    wifi_config.ap.ssid_len += 4;

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(tag, "wifi_init_softap finished.SSID:%s password:%s", wifi_config.ap.ssid, wifi_config.ap.password);
}

void wifi_ap_init(void)
{
    wifi_init_softap();
}

//as client:
//1: netconn_new()
//2: netconn_connect()
//3: netconn_write()
//4: netconn_recv()
//5: netconn_close()
//6: netconn_getaddr()
//7: netconn_delete()
//
//as server
//1: netconn_new()
//2: netconn_bind()
//3: netconn_listen()
//4: netconn_accept()
//5: netconn_write()
//6: netconn_recv()
//7: netconn_close()
//8: netconn_getaddr()
//9: netconn_delete()

void wifi_data_interactive_task(void *pvParameter)
{
    err_t err;
    struct netconn *newconn;
    ip_addr_t ipaddr;
    u16_t     port;
    struct netbuf *recvbuf;
    struct netconn *tcp_conn = netconn_new(NETCONN_TCP);
    err = netconn_bind(tcp_conn,IP_ADDR_ANY,TCPIP_PORT);
    if(ERR_OK != err)
    {
        ESP_LOGE(tag, "netconn_bind failed(%d)",err);
    }
    err = netconn_listen(tcp_conn);
    if(ERR_OK != err)
    {
        ESP_LOGE(tag, "netconn_listen failed(%d)",err);
    }
    ESP_LOGI(tag, "start listen on %d",TCPIP_PORT);

    while(1)
    {
        err = netconn_accept(tcp_conn,&newconn);  //接收连接请求
        newconn->recv_timeout = 10;
        if(ERR_OK == err)
        {
            netconn_getaddr(newconn,&ipaddr,&port,0); //获取远端IP地址和端口号
            ESP_LOGI(tag, "new connect: %d.%d.%d.%d:%d",(ipaddr.u_addr.ip4.addr&0xff),(ipaddr.u_addr.ip4.addr>>8)&0xff,
                                                        (ipaddr.u_addr.ip4.addr>>16)&0xff,(ipaddr.u_addr.ip4.addr>>24)&0xff,port);
            while(1)
            {
                struct pbuf *q = NULL;
                uint32_t data_len = 0;
                #define TCP_SERVER_RX_BUFSIZE  128
                uint8_t tcp_server_recvbuf[TCP_SERVER_RX_BUFSIZE] = {0};

                if((err = netconn_recv(newconn,&recvbuf)) == ERR_OK)        //接收到数据
                {        
                    memset(tcp_server_recvbuf,0,TCP_SERVER_RX_BUFSIZE);     //数据接收缓冲区清零
                    for(q = recvbuf->p; q != NULL;q = q->next)              //遍历完整个pbuf链表
                    {
                        //判断要拷贝到TCP_SERVER_RX_BUFSIZE中的数据是否大于TCP_SERVER_RX_BUFSIZE的剩余空间，如果大于
                        //的话就只拷贝TCP_SERVER_RX_BUFSIZE中剩余长度的数据，否则的话就拷贝所有的数据

                        //拷贝数据
                        if(q->len > (TCP_SERVER_RX_BUFSIZE-data_len)) 
                            memcpy(tcp_server_recvbuf+data_len,q->payload,(TCP_SERVER_RX_BUFSIZE-data_len));
                        else 
                            memcpy(tcp_server_recvbuf+data_len,q->payload,q->len);
                        data_len += q->len;      
                        //超出TCP客户端接收数组,跳出
                        if(data_len > TCP_SERVER_RX_BUFSIZE) 
                            break; 
                    }
                    printf("wifi recv: %s\r\n",tcp_server_recvbuf);  //通过串口发送接收到的数据
                    netbuf_delete(recvbuf);
                }
                else if(err == ERR_CLSD)  //关闭连接
                {
                    netconn_close(newconn);
                    netconn_delete(newconn);
                    break;
                }

                err = netconn_write(newconn,tcp_server_recvbuf,data_len,NETCONN_COPY); //发送tcp_server_sendbuf中的数据
                if(err != ERR_OK)
                {
                    ESP_LOGE(tag, "wifi send failed(%d)",err);
                }
            }
        }
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
}
#endif


